package net.piggy.websocket.server;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.AttributeKey;
import java.net.URI;
import java.util.Arrays;

public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        Channel channel0 = ctx.channel();
        Channel channel1 = (Channel) channel0.attr(AttributeKey.valueOf("channel")).get();
        channel1.close();
        channel0.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
        if (frame instanceof TextWebSocketFrame) {
            String request = ((TextWebSocketFrame) frame).text();
            Object o = ctx.channel().attr(AttributeKey.valueOf("channel")).get();
            Channel channel = (Channel) o;
            channel.writeAndFlush(new TextWebSocketFrame(request));

            System.out.println("receive and forward --> " + request);
        }else if(frame instanceof BinaryWebSocketFrame){
            ByteBuf buf = frame.content();
            byte[] array = new byte[buf.readableBytes()];
            buf.getBytes(buf.readerIndex(), array);
            System.out.println(Arrays.toString(array));

            buf.retain();
            String message = buf.toString(io.netty.util.CharsetUtil.UTF_8);
            System.out.println(message);
            buf.release();
        } else {
            String message = "unsupported frame type: " + frame.getClass().getName();
            throw new UnsupportedOperationException(message);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {

            String URL = System.getProperty("url", "ws://127.0.0.1:8888/websocket");
            WebSocketServerProtocolHandler.HandshakeComplete evt1 = (WebSocketServerProtocolHandler.HandshakeComplete) evt;
            URI uri = new URI(URL);
            String scheme = uri.getScheme() == null ? "ws" : uri.getScheme();
            final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
            final int port;
            if (uri.getPort() == -1) {
                if ("ws".equalsIgnoreCase(scheme)) {
                    port = 80;
                } else if ("wss".equalsIgnoreCase(scheme)) {
                    port = 443;
                } else {
                    port = -1;
                }
            } else {
                port = uri.getPort();
            }

            if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
                System.err.println("Only WS(S) is supported.");
                return;
            }

            final boolean ssl = "wss".equalsIgnoreCase(scheme);
            final SslContext sslCtx;
            if (ssl) {
                sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            } else {
                sslCtx = null;
            }

            EventLoopGroup group = new NioEventLoopGroup();
            WebSocketClientHandshaker shaker = WebSocketClientHandshakerFactory.newHandshaker(uri,
                    WebSocketVersion.V13, "piggy", true, new DefaultHttpHeaders());
            WebSocketClientHandler handler = new WebSocketClientHandler(shaker);
            Bootstrap b = new Bootstrap();
                b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) {
                                ChannelPipeline p = ch.pipeline();
                                if (sslCtx != null) {
                                    p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
                                }
                                p.addLast(new HttpClientCodec());
                                p.addLast(new HttpObjectAggregator(65536));
                                p.addLast(WebSocketClientCompressionHandler.INSTANCE);
                                p.addLast(handler);

                            }
                        });

                Channel ch = b.connect(uri.getHost(), port).sync().channel();
                handler.handshakeFuture().sync();

                ctx.channel().attr(AttributeKey.valueOf("channel")).set(ch);

                ch.attr(AttributeKey.valueOf("channel")).set(ctx.channel());

        }
    }
}
